#include "keyboard.h"
#include "console.h"
#include "kernel.h"
#include "queue.h"
#include "stdio.h"
#include "scheduler.h"
#include "libasm.h"

#define is_make_code(x)   ( (x) < 128 )
#define is_break_code(x)  ( (x) >= 128 )
#define is_graphical(x)	  ( kb_scancodes[x][0] )
#define is_alt(x)         ( (x) == 56 )
#define is_ctrl(x)         ( (x) == 29 )
#define is_c_letter(x)         ( (x) == 46 )
#define is_shift_on(x)    ( ((x) & (KEY_LSHIFT + KEY_RSHIFT)) != 0 )
#define is_caps_on(x)     ( ((x) & KEY_CAPS) != 0 )
#define is_num_on(x)     ( ((x) & KEY_NUM) != 0 )
#define is_scr_on(x)     ( ((x) & KEY_SCR) != 0 )


static int get_function_key(unsigned char scancode);
static int ignore_scancode(unsigned char scancode);
static int get_special_key(unsigned char scancode);
static int get_modifier_key(unsigned char scancode);
static void handle_graphical_key(unsigned char scancode,
		keyT *rta, int modifiers);


static char alt = 0;
static char ctrl = 0;

/* Matriz con los scancodes
 * ------------------------
 * Columna 0: ascii del scancode
 * Columna 1: ascii del scancode en mayusculas, o 0 si no aplica
 * Columna 2: 0 si shift tiene el mismo comportamiento que capslock, 1 eoc
 * Columna 3: 1 si es caracter es "imprimible" o 0 si no.
 */

static unsigned char kb_scancodes[128][4] = {
	{0, 	0, 	0, 	0},
	{27, 	0, 	0, 	0},
	{'1', 	'!', 	1, 	1},
	{'2', 	'@', 	1, 	1},
	{'3', 	'#', 	1, 	1},
	{'4', 	'$',	1, 	1},
	{'5', 	'%', 	1, 	1},
	{'6', 	'^', 	1, 	1},
	{'7', 	'&', 	1, 	1},
	{'8', 	'*', 	1, 	1},
	{'9', 	'(', 	1, 	1},
	{'0', 	')', 	1, 	1},
	{'-', 	'_', 	1, 	1},
	{'=', 	'+', 	1, 	1},
	{'\b', 	'\b',	0, 	0},	// 14 - Backspace
	{'\t', 	'\t', 	0, 	0},	// 15 - Tab
	{'q', 	'Q', 	0, 	1},
	{'w', 	'W', 	0, 	1},
	{'e', 	'E', 	0, 	1},
	{'r', 	'R', 	0, 	1},
	{'t', 	'T', 	0, 	1},
	{'y', 	'Y', 	0, 	1},
	{'u', 	'U', 	0, 	1},
	{'i',	'I', 	0, 	1},
	{'o', 	'O', 	0, 	1},
	{'p', 	'P', 	0, 	1},
	{'[', 	'{', 	1, 	1},
	{']', 	'}', 	1, 	1},
	{'\n', 	'\n', 	0, 	0},	// 28 - Enter
	{0, 	0, 	0, 	0},	// 29 - Control
	{'a', 	'A', 	0, 	1},
	{'s', 	'S', 	0, 	1},
	{'d', 	'D', 	0, 	1},
	{'f', 	'F', 	0, 	1},
	{'g', 	'G', 	0, 	1},
	{'h', 	'H', 	0, 	1},
	{'j', 	'J', 	0, 	1},
	{'k',	'K', 	0, 	1},
	{'l', 	'L', 	0, 	1},
	{';', 	':', 	1, 	1},
	{'\'', 	'"', 	1, 	1},
	{'`', 	'~', 	1, 	1},
	{0, 	0, 	0, 	0},	// 42 - Left shift
	{'\\',	'|', 	1, 	1},
	{'z', 	'Z', 	0, 	1},
	{'x', 	'X',	0,	1},
	{'c', 	'C', 	0, 	1},
	{'v', 	'V', 	0, 	1},
	{'b', 	'B', 	0, 	1},
	{'n', 	'N', 	0, 	1},
	{'m', 	'M', 	0, 	1},
	{',', 	'<', 	1, 	1},
	{'.',	'>', 	1, 	1},
	{'/', 	'?', 	1, 	1},
	{0, 	0, 	0, 	0},	// 54 - Right shift
	{'*', 	0, 	0, 	1},
	{0, 	0, 	0, 	0},	// 56 - Alt
	{' ', 	' ', 	0, 	1}, 	// 57 - Space bar
	{0, 	0, 	0, 	0},  	// 58 - Caps lock
	{0, 	0,	0, 	0}, 	// 59 - F1 ... >
	{0, 	0,	0, 	0},
	{0,	0,	0,	0},
	{0,	0,	0,	0},
	{0,	0,	0, 	0},
	{0,	0,	0, 	0},
	{0,	0,	0,	0},
	{0,	0,	0,	0},
	{0,	0,	0,	0},
	{0,	0,	0, 	0}, 	// 68 - < ... F10
	{0,	0,	0, 	0}, 	// 69 - Num lock
	{0,	0,	0, 	0}, 	// 70 - Scroll Lock
	{0,	0,	0, 	0}, 	// 71 - Home
	{0,	0,	0, 	0}, 	// 72 - Up Arrow
	{0,	0,	0, 	0}, 	// 73 - Page Up
	{'-', 	'_', 	1, 	1},
	{0, 	0,	0,	0},	// 75 - Left Arrow
	{0,	0,	0, 	0},
	{0,	0,	0, 	0},  	// 77 - Right Arrow
	{'+', 	'*', 	1, 	1},
	{0, 	0,	0, 	0}, 	// 79 - End key
	{0,	0,	0, 	0},	// 80 - Down Arrow
	{0, 	0,	0, 	0}, 	// 81 - Page Down
	{0, 	0,	0, 	0}, 	// 82 - Insert Key
	{0, 	0,	0, 	0}, 	// 83 - Delete Key
	{0, 	0,	0, 	0},
	{0,	0,	0,	0},
	{0,	0,	0, 	0},
	{0, 	0,	0,	0}, 	// 87 - F11 Key
	{0, 	0,	0,	0}, 	// 88 - F12 Key
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},	// 90
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},	// 100
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},	// 110
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},	// 120
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0},
	{0, 	0, 	0, 	0}
};


int
handle_scancode(unsigned char scancode)
{
	/*_Cli();*/ /*MODIFICADO*/
	int new_tty; /* Consola a la que voy a cambiar cuando toquen ALT+F... */


	/* Si el scancode esta en mi lista de scancodes a ignorar, lo ignoro */
	if ( ignore_scancode(scancode) )
	       return -1;

	/* Me fijo si el usuario toco alguna tecla de funcion */
	if ( (new_tty = get_function_key(scancode)) && alt)
		return new_tty - 1;

	/* Despertar el proceso foreground de la terminal activa  */
	scancode_arrived();

	/* Me fijo si vino alt, para setear mi flag local */
	if ( is_make_code(scancode) && is_alt(scancode) )
		alt = 1;
	if ( is_break_code(scancode) && is_alt(scancode-128))
		alt = 0;

	if ( is_make_code(scancode) && is_ctrl(scancode) )
		ctrl = 1;
	if ( is_break_code(scancode) && is_ctrl(scancode-128))
		ctrl = 0;
	if(is_c_letter(scancode) && ctrl)
	{
		_Cli();
		siginthandler();
		//_Sti();
		return -1;
	}

	/* Encolo el scancode que llego */
	if ( !sc_queue_is_full() )
		sc_enqueue(scancode);



	return -1;
}


keyT
get_key(void)
{
	keyT rta;
	int c;
	unsigned char sc;
	int modifiers;

	/* Teclas modificadoras del teclado de la consola activa */
	modifiers = get_keyboard_modifiers();

	/* Valores por default de respuesta */
	rta.value = KEY_NULL;
	rta.modifiers = 0;

	/* Voy desencolando cosas de la cola hasta sacar un caracter
	 * imprimible o vaciar la cola */
	while (!sc_queue_is_empty() &&
			rta.value == KEY_NULL )
	{
		sc_dequeue(&sc);

		if ( is_make_code(sc) )
		{
			/* Me fijo si es una tecla especial */
			if ( (c = get_special_key(sc)) )
			{
				rta.value = c;
				rta.modifiers = modifiers;
				return rta;
			}

			/* Me fijo si es una tecla modificadora */
			else if ( (c = get_modifier_key(sc)) )
			{
				/* Marco la tecla modificadora */
				if (c == KEY_CAPS)
				{
					if ( is_caps_on(modifiers) )
						set_keyboard_modifiers( modifiers &= (~KEY_CAPS));
					else
						set_keyboard_modifiers( modifiers |= KEY_CAPS);
				}
				else if (c == KEY_NUM)
				{
					if ( is_num_on(modifiers) )
						set_keyboard_modifiers( modifiers &= (~KEY_NUM));
					else
						set_keyboard_modifiers( modifiers |= KEY_NUM);
				}
				else if (c == KEY_SCR)
				{
					if ( is_scr_on(modifiers) )
						set_keyboard_modifiers( modifiers &= (~KEY_SCR));
					else
						set_keyboard_modifiers( modifiers |= KEY_SCR);
				}
				else
					set_keyboard_modifiers( modifiers |= c );

				setLights(modifiers);
			}

			/* Si es una tecla grafica, la guardo en el buffer  */
			else if ( is_graphical(sc) )
				handle_graphical_key(sc, &rta, modifiers );


			/* Si no era ninguna de las anteriores, la ignoro */
		}
		else
		{
			sc -= 128;

			/* Me fijo si es una tecla modificadora */
			if ( (c = get_modifier_key(sc)) )
			{
				/* Obs: Aca nunca va a llegar el break code
				 * del caps lock, porque lo filtra antes
				 * la funcion ignore_scancode
				 */

				set_keyboard_modifiers( modifiers &= (~c) );
			}
		}
	}
	return rta;
}




/* Implementacion de funciones privadas */


/* Funcion: handle_graphical_key
 * -----------------------------
 * Procesa una tecla que tiene representacion grafica en la pantalla.
 * (includos backspace, tab, enter, etc...)
 */

static void
handle_graphical_key(unsigned char sc, keyT *rta, int modifiers)
{
	if ( is_caps_on(modifiers) )
	{
		/* Si shift no tiene el mismo comportamiento que capslock */
		if ( kb_scancodes[sc][2] )
		{
			rta->value = is_shift_on(modifiers) ?
				kb_scancodes[sc][1] : kb_scancodes[sc][0];
		}
		else
		{
			rta->value = is_shift_on(modifiers) ?
				kb_scancodes[sc][0] : kb_scancodes[sc][1];
		}
	}
	else
	{
		rta->value = is_shift_on(modifiers) ?
			kb_scancodes[sc][1] : kb_scancodes[sc][0];
	}
	rta->modifiers = modifiers;
	return;
}


/* Funcion: get_function_key
 * -------------------------
 * Retorna 0 si el scancode no pertecene a una tecla de funcion o
 * el numero de tecla si pertenece.
 */

static int
get_function_key(unsigned char scancode)
{
	/* Si esta entre F1 y F10 */
	if ( scancode >= 59 && scancode <= 68 )
		return (scancode - 58);

	/* Si es F11 o F12 */
	if ( scancode == 87 || scancode == 88 )
		return (scancode - 76);

	/* No es tecla de funcion */
	return 0;
}


/* Funcion: ignore_scancode
 * ------------------------
 * Retorna 1 si el scancode debe ser ignorado, 0 en caso contrario.
 */

static int
ignore_scancode(unsigned char scancode)
{
	/* Las teclas alt y ctrl de la derecha envian un scancode
	 * 224 antes de su propio scancode.  Como no me interesa
	 * diferenciarlas lo ignoro. */

	if (scancode == 224)
		return 1;

	/* El break code del capslock */
	if (scancode == (58 + 128))
		return 1;
	/* El break code del numlock */
	if (scancode == (69 + 128))
		return 1;
	/* El break code del scrlock */
	if (scancode == (70 + 128))
		return 1;

	/* Los break codes de teclas graficas no me interesan. */
	if ( (scancode >= 128) && is_graphical(scancode-128))
		return 1;

	return 0;
}


/* Funcion: get_special_key
 * ------------------------
 * Retorna la cte simbolica de la tecla especial presionada o
 * 0 si el scancode no corresponde a ninguna tecla especial.
 */

static int
get_special_key(unsigned char scancode)
{
	/* Este codigo esta hecho asi y no con switch porque sino
	 * el grub no carga el binario generado.
	 */

	if (scancode == 71)
		return KEY_HOME;
	else if (scancode == 75)
		return KEY_LEFT;
	else if (scancode == 77)
		return KEY_RIGHT;
	else if (scancode == 79)
		return KEY_END;
	else if (scancode == 83)
		return KEY_DELETE;
	else if (scancode == 1)
		return KEY_ESC;
	else if (scancode == 72)
		return KEY_UP;
	else if (scancode == 80)
		return KEY_DOWN;
	else
		return 0;
}


/* Funcion: get_modifier_key
 * -------------------------
 * Retorna la cte simbolica de la tecla modificadora presionada
 * o 0 si el scancode no correswponde con ninguna.
 */

static int
get_modifier_key(unsigned char scancode)
{
	if (scancode >= 128)
		scancode -= 128;

	/* Este codigo esta hecho asi y no con switch porque sino
	 * el grub no carga el binario generado.
	 */

	if ( scancode == 29 )
		return KEY_LCTRL;
	else if ( scancode == 42 )
		return KEY_LSHIFT;
	else if ( scancode == 54 )
		return KEY_RSHIFT;
	else if ( scancode == 56 )
		return KEY_LALT;
	else if ( scancode == 58 )
		return KEY_CAPS;
	else if ( scancode == 69 )
		return KEY_NUM;
	else if ( scancode == 70 )
		return KEY_SCR;

	return 0;
}

void
setLights(int modifiers)
{
	char keys= 0;
	
	if (is_scr_on(modifiers))
		keys |= 1;
	if (is_num_on(modifiers))
		keys |= 2;
	if (is_caps_on(modifiers))
		keys |= 4;

	_Lights(keys);
	return;
}
